/*************************************************************
 * File: Cryptogram.java
 * Copyright (c) 2012
 * Author: Shaobin.Software@gmail.com Shaobin_Software@163.com 
 * Date: Jan 22, 2013
 * Description: 
 ************************************************************/
package snow.core.toolkit.security;

import java.security.MessageDigest;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import snow.core.toolkit.StringHelper;

/**
 *
 */
public class Cryptogram {

	private static final Log log = LogFactory.getLog(Cryptogram.class);

	private static final byte[] DEFAULT_IV = { 1, 2, 3, 4, 5, 6, 7, 8 };

	/**
	 * 使用BASE64解码
	 * 
	 * @param input
	 * @return
	 */
	public static byte[] decodeByBase64(byte[] input) {
		return input == null ? null : Base64.decodeBase64(input);
	}

	/**
	 * 使用BASE64解码
	 * 
	 * @param input
	 * @return
	 */
	public static String decodeByBase64(String input) {
		final byte[] bytes = input == null ? null : decodeByBase64(input.getBytes());
		return bytes == null ? null : new String(bytes);
	}

	/**
	 * 使用3DES解密
	 * 
	 * @param input
	 * @param key
	 * @return
	 */
	private static byte[] decryptBy3DES(byte[] input, String key) {
		return doFinalBy3DESFrom(input, key, Cipher.DECRYPT_MODE);
	}

	/**
	 * 使用3DES解密
	 * 
	 * @param input
	 * @param key
	 * @return
	 */
	public static String decryptBy3DES(String input, String key) {
		if (input == null || key == null) {
			return null;
		}
		byte[] result = decryptBy3DES(decodeByBase64(input.getBytes()), key);
		return result == null ? null : new String(result);
	}

	protected static byte[] doFinalBy3DESFrom(byte[] input, String key, int mode) {
		try {
			final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
			final SecretKeySpec secretKeySpec = new SecretKeySpec(getKeyFor3DESFrom(key), "DESede");
			final IvParameterSpec ivParameterSpec = new IvParameterSpec(getIVFor3DESFrom(key));
			cipher.init(mode, secretKeySpec, ivParameterSpec);
			return cipher.doFinal(input);
		} catch (Exception e) {
			log.warn(e.getMessage(), e);
			return null;
		}
	}

	/**
	 * 使用BASE64编码
	 * 
	 * @param input
	 * @return
	 */
	public static byte[] encodeByBase64(byte[] input) {
		return input == null ? null : Base64.encodeBase64(input);
	}

	/**
	 * 使用BASE64编码
	 * 
	 * @param input
	 * @return
	 */
	public static String encodeByBase64(String input) {
		final byte[] bytes = input == null ? null : encodeByBase64(input.getBytes());
		return bytes == null ? null : new String(bytes);
	}

	/**
	 * 使用MD5编码
	 * 
	 * @param input
	 * @param toLowerCase
	 * @return
	 */
	public static String encodeByMD5(byte[] input, boolean toLowerCase) {
		return encodeByMessageDigest("MD5", input, toLowerCase);
	}

	/**
	 * 使用MD5编码
	 * 
	 * @param input
	 * @return
	 */
	public static String encodeByMD5(String input) {
		return input == null ? null : encodeByMD5(input.getBytes(), true);
	}

	protected static String encodeByMessageDigest(String algorithm, byte[] input, boolean toLowerCase) {
		try {
			final MessageDigest digest = MessageDigest.getInstance(algorithm);
			final String encoded = new String(Hex.encodeHex(digest.digest(input)));
			return toLowerCase ? encoded.toLowerCase() : encoded.toUpperCase();
		} catch (Exception e) {
			log.warn(e.getMessage(), e);
			return null;
		}
	}

	/**
	 * 使用SHA-1编码
	 * 
	 * @param input
	 * @return
	 */
	public static String encodeBySHA(byte[] input, boolean toLowerCase) {
		return encodeByMessageDigest("SHA-1", input, toLowerCase);
	}

	/**
	 * 使用SHA-1编码
	 * 
	 * @param input
	 * @return
	 */
	public static String encodeBySHA(String input) {
		return input == null ? null : encodeBySHA(input.getBytes(), true);
	}

	/**
	 * 使用3DES加密
	 * 
	 * @param input
	 * @param key
	 * @return
	 */
	private static byte[] encryptBy3DES(byte[] input, String key) {
		return doFinalBy3DESFrom(input, key, Cipher.ENCRYPT_MODE);
	}

	/**
	 * 使用3DES加密
	 * 
	 * @param input
	 * @param key
	 * @return
	 */
	public static String encryptBy3DES(String input, String key) {
		final byte[] bytes = input == null ? null : encryptBy3DES(input.getBytes(), key);
		return bytes == null ? null : new String(encodeByBase64(bytes));
	}

	public static byte[] getIVFor3DESFrom(Object object) {
		if (object != null) {
			final int hashCode = object.hashCode();
			final byte[] iv = new byte[8];
			for (int i = 0; i < 8; i++) {
				iv[i] = (byte) (hashCode >> DEFAULT_IV[i]);
			}
			return iv;
		}
		return DEFAULT_IV;
	}

	public static byte[] getKeyFor3DESFrom(String key) {
		final int n = 24;
		final byte[] bytes = new byte[n];
		final String text = StringHelper.format(key, n, '0');
		for (int i = 0; i < n; i++) {
			final char c = text.charAt(i % text.length());
			bytes[i] = (byte) (c >= '0' && c <= '9' ? c % '0' : c >= 'A' && c <= 'F' ? c % 'A' + 10 : c >= 'a' && c <= 'f' ? c % 'a' + 10 : 0x00);
		}
		return bytes;
	}

}
